package pl.polsl.mmm.projectcomm.util;

import android.content.Context;
import android.content.res.Resources;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import static android.opengl.GLES20.*;

import pl.polsl.mmm.projectcomm.data.*;
import pl.polsl.mmm.projectcomm.objects.MD5.MD5;
import pl.polsl.mmm.projectcomm.objects.MD5.MD5Action;
import pl.polsl.mmm.projectcomm.objects.MD5.MD5Joint;
import pl.polsl.mmm.projectcomm.objects.MD5.MD5Vertex;
import pl.polsl.mmm.projectcomm.objects.MD5.MD5Weight;

/**
 * Created by Maciek on 2014-11-01.
 * Klasa statyczna do wczytywania modelu i animacji z plików md5
 */
public class MD5Parser {
    /**
     * funkcja wczytująca Mesha ze wskazanego pliku
     * */
    public static MD5 loadMesh(Context context, int resourceId) {
        MD5 md5 = new MD5();
        md5.setVisible(true);

        int intVal;
        int meshIndex = 0;

        try {
            InputStream inputStream = context.getResources().openRawResource(resourceId);
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            String nextLine;
            while ((nextLine = bufferedReader.readLine()) != null) {
                if (nextLine.startsWith("MD5Version")) {
                    //sprawdzenie czy plik jest w wersji 10 MD5
                    String[] splittedLine = nextLine.split(" ");
                    if (Integer.parseInt(splittedLine[1]) != 10) {
                        //jeśli nie , przerywa operacje, zwraca null
                        return null;
                    }
                } else if (nextLine.startsWith("numJoints")) {
                    //pobranie danej o ilości kości w modelu
                    String[] splittedLine = nextLine.split(" ");
                    int nJoints = Integer.parseInt(splittedLine[1]);
                    //zaalokowanie tabeli z kośćmi uzyskaną ilością
                    if (nJoints != 0)
                        md5.alocateBindPose(nJoints);
                } else if (nextLine.startsWith("numMeshes")) {
                    //pobranie danej o ilości mesh'ów ( mesh'y? )
                    String[] splittedLine = nextLine.split(" ");
                    int nMesh = Integer.parseInt(splittedLine[1]);
                    if (nMesh != 0) {
                        md5.alocateMesh(nMesh);
                        for(int i=0;i<nMesh;i++) {
                            md5.md5mesh[i].setMode(GL_TRIANGLES);
                            md5.md5mesh[i].setVisible(true);
                        }
                    }
                } else if (nextLine.startsWith("joints {")) {
                    int i = 0;
                    //wejście do bloku z danymi kości
                    //wczytujemy od razu kolejną linie
                    nextLine = bufferedReader.readLine();
                    //petla sprawdza zakończenie bloku (znakiem '}' )
                    while (!nextLine.startsWith("}")) {
                        //przyciecie wczytanej linii do znaków "//"
                        //tam zakomentowana jest nazwa rodzica, której nie potrzebujemy (mamy jego identyfikator)
                        int trimLoc = nextLine.lastIndexOf("//");
                        nextLine = nextLine.substring(0, trimLoc);
                        //nextLine.replace('\t',' ');
                        //podział stringa. Znaki rozdzielające to " " , "(", ")" oraz "\t" (tabulator)
                        String[] splittedLine = nextLine.split("[ ()\t]+");
                        //uwzględniamy jedynie te linie które zawierają 8 danych
                        if (splittedLine.length == 9) {
                            //nazwa Jointa - trzeba usunać ' " ' stąd substring
                            String name = splittedLine[1].substring(1, splittedLine[1].length() - 1);
                            int parent = Integer.parseInt(splittedLine[2]);
                            vec3 location = new vec3(Float.parseFloat(splittedLine[3]),
                                    Float.parseFloat(splittedLine[4]),
                                    Float.parseFloat(splittedLine[5]));

                            vec4 rotation = new vec4(Float.parseFloat(splittedLine[6]),
                                    Float.parseFloat(splittedLine[7]),
                                    Float.parseFloat(splittedLine[8]));
                            //wyznaczenie składowej 'w' wektora
                            rotation.buildW();
                            md5.bindPose[i] = new MD5Joint(name, parent, location, rotation);
                            ++i;
                        }
                        nextLine = bufferedReader.readLine();
                    }
                } else if (nextLine.startsWith("mesh {")) {
                    MD5Vertex vertex = new MD5Vertex();
                    MD5Weight weight = new MD5Weight();
                    //wejście do bloku z danymi mesha
                    //wczytujemy od razu kolejną linie
                    nextLine = bufferedReader.readLine();
                    while (!nextLine.startsWith("}")) {
                        if(nextLine.startsWith("\tshader")) {
                            String[] splittedLine = nextLine.split(" ");
                            //usunięcie znaków "" ze stringa
                            String textureName = splittedLine[1].substring(1, splittedLine[1].length()-1);
                            md5.md5mesh[meshIndex].setTextureName(textureName);
                        } else if (nextLine.startsWith("\tnumverts")) {
                            String[] splittedLine = nextLine.split(" ");
                            md5.md5mesh[meshIndex].alocateVertex(Integer.parseInt(splittedLine[1]));
                        } else if (nextLine.startsWith("\tvert")) {
                            String[] splittedLine = nextLine.split("[ ()\t]+");
                            intVal = Integer.parseInt(splittedLine[2]);
                            float u = Float.parseFloat(splittedLine[3]);
                            float v = Float.parseFloat(splittedLine[4]);
                            vertex.setUv(new vec2(u,v));
                            vertex.setStartWeight(Integer.parseInt(splittedLine[5]));
                            vertex.setWeightCount(Integer.parseInt(splittedLine[6]));
                            md5.md5mesh[meshIndex].insertVertex(vertex,intVal);
                        } else if (nextLine.startsWith("\tnumtris")) {
                            String[] splittedLine = nextLine.split(" ");
                            int nTriangle = Integer.parseInt(splittedLine[1]);
                            md5.md5mesh[meshIndex].setnIndice(nTriangle*3);
                            md5.md5mesh[meshIndex].alocateTriangle(nTriangle);
                        } else if (nextLine.startsWith("\ttri")) {
                            String[] splittedLine = nextLine.split(" ");
                            intVal =  Integer.parseInt(splittedLine[1]);
                            short x = Short.parseShort(splittedLine[2]);
                            short y = Short.parseShort(splittedLine[3]);
                            short z = Short.parseShort(splittedLine[4]);
                            md5.md5mesh[meshIndex].insertTriangle(x,y,z,intVal);
                        } else if (nextLine.startsWith("\tnumweights")) {
                            String[] splittedLine = nextLine.split(" ");
                            md5.md5mesh[meshIndex].alocateWeight(Integer.parseInt(splittedLine[1]));
                        } else if (nextLine.startsWith("\tweight")) {
                            String[] splittedLine = nextLine.split("[ ()]+");
                            intVal = Integer.parseInt(splittedLine[1]);
                            weight.setJoint(Integer.parseInt(splittedLine[2]));
                            weight.setBias(Float.parseFloat(splittedLine[3]));
                            float x = Float.parseFloat(splittedLine[4]);
                            float y = Float.parseFloat(splittedLine[5]);
                            float z = Float.parseFloat(splittedLine[6]);
                            weight.setLocation(new vec3(x,y,z));
                            md5.md5mesh[meshIndex].insertWeight(weight,intVal);
                        }
                        //odczytanie kolejnej linii
                        nextLine = bufferedReader.readLine();
                    }
                    //zaalokowanie tablicy indeksów
                    md5.md5mesh[meshIndex].alocateIndice();
                    //skopiowanie całej tablicy trójkątów do tablicy indices
                    md5.md5mesh[meshIndex].insertIndices();
                    ++meshIndex;
                }
            }
            return md5;
        } catch (IOException e) {
            throw new RuntimeException("Could not open resource: " + resourceId, e);
        } catch (Resources.NotFoundException nfe) {
            throw new RuntimeException(" Resource not found: " + resourceId, nfe);
        }
    }
    /**
    *   Funkcja wczytujaca animacje ze wskazanego pliku
    * */
    public static int loadAction(MD5 md5, String name, Context context, int resourceId) {
        MD5Action action = new MD5Action();
        md5.incnAction();
        int intVal;
        action.setCurr_frame(0);
        action.setNext_frame(1);
        action.setName(name);

        try {
            String nextLine;
            InputStream inputStream = context.getResources().openRawResource(resourceId);
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            while ((nextLine = bufferedReader.readLine()) != null) {
                if (nextLine.startsWith("MD5Version")) {
                    //sprawdzenie czy plik jest w wersji 10 MD5
                    String[] splittedLine = nextLine.split(" ");
                    if (Integer.parseInt(splittedLine[1]) != 10) {
                        //jeśli nie , przerywa operacje, zwraca null
                        return -1;
                    }
                } else if (nextLine.startsWith("numFrames")) {
                    String[] splittedLine = nextLine.split(" ");
                    action.alocateFrame(Integer.parseInt(splittedLine[1]), md5.getnJoint());
                } else if (nextLine.startsWith("numJoints")) {
                    String[] splittedLine = nextLine.split(" ");
                    int nJoint = Integer.parseInt(splittedLine[1]);
                    if (nJoint != md5.getnJoint())
                        return -1;
                    action.alocatePose(nJoint);
                    for (int i = 0; i < md5.getnJoint(); i++) {
                        action.getPose()[i].setName(md5.bindPose[i].getName());
                    }
                } else if (nextLine.startsWith("frameRate")) {
                    String[] splittedLine = nextLine.split(" ");
                    action.setFps(1.0f / (float) Integer.parseInt(splittedLine[1]));
                } else if (nextLine.startsWith("frame")) {
                    String[] splittedLine = nextLine.split(" ");
                    intVal = Integer.parseInt(splittedLine[1]);
                    //MD5Joint[] joint = action.getFrame()[intVal];
                    nextLine = bufferedReader.readLine();
                    int i = 0;
                    while (!nextLine.startsWith("}")) {
                        String noTabLine = nextLine.substring(1,nextLine.length()-1);
                        float xLocation, yLocation, zLocation;
                        float xRotation, yRotation, zRotation;
                        splittedLine = noTabLine.split(" ");

                        xLocation = Float.parseFloat(splittedLine[0]);
                        yLocation = Float.parseFloat(splittedLine[1]);
                        zLocation = Float.parseFloat(splittedLine[2]);

                        xRotation = Float.parseFloat(splittedLine[3]);
                        yRotation = Float.parseFloat(splittedLine[4]);
                        zRotation = Float.parseFloat(splittedLine[5]);


                        action.getFrame()[intVal][i].setLocation(
                                new vec3(xLocation, yLocation, zLocation));
                        action.getFrame()[intVal][i].setRotation(
                                new vec3(xRotation, yRotation, zRotation));

                        action.getFrame()[intVal][i].setName(md5.bindPose[i].getName());
                        ++i;
                        nextLine = bufferedReader.readLine();
                    }
                    vec3 location = new vec3();
                    vec4 rotation = new vec4();
                    for (i = 0; i < md5.getnJoint(); i++) {
                        if (md5.bindPose[i].getParent() > -1) {
                            MD5Joint joint = action.getFrame()[intVal][md5.bindPose[i].getParent()];
                            location.rotateVec4(
                                    action.getFrame()[intVal][i].getLocation(),
                                    joint.getRotation()
                            );
                            action.getFrame()[intVal][i].setLocation(new vec3(
                                    location.x + joint.getLocation().x,
                                    location.y + joint.getLocation().y,
                                    location.z + joint.getLocation().z
                            ));
                            rotation.multiplyVec4(
                                    joint.getRotation(),
                                    action.getFrame()[intVal][i].getRotation()
                            );
                            action.getFrame()[intVal][i].setRotation(new vec4(rotation));
                        }
                    }
                }

            }
            /** Uzupełnienie danych o zależności kości między sobą */
            for(int i=0;i<md5.getnJoint();i++) {
                action.getPose()[i].setParent(md5.bindPose[i].getParent());
            }
            md5.md5action.add(action);

            return md5.getnAction();
        } catch (IOException e) {
            throw new RuntimeException("Could not open resource: " + resourceId, e);
        } catch (Resources.NotFoundException nfe) {
            throw new RuntimeException(" Resource not found: " + resourceId, nfe);
        }
    }
}
